﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace MupdfSharp
{
    public class PdfRenderRealtime : IDisposable
    {
        private const uint FZ_STORE_DEFAULT = 256 << 20;
        // Track whether Dispose has been called. 
        private bool disposed = false;
        private IntPtr context;
        private IntPtr fileStream;
        private IntPtr documentStream;
        private int pageCount;
        private string datapath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), typeof(PdfRenderRealtime).Name);
        //A4纸的尺寸是210mm*297mm
        private SizeF bitmapSize = new SizeF(Convert.ToSingle(21.0 * SystemDPIHelper.dpi_x / 2.54), Convert.ToSingle(29.7 * SystemDPIHelper.dpi_y / 2.54));
        private int currentPage = -1;
        private string currentDoc = "";
        //private Dictionary<int, string> pageCacheDict = new Dictionary<int, string>();
        private Dictionary<int, Bitmap> pageCacheDict = new Dictionary<int, Bitmap>();
        // private static bool IsFirst = true;
        //public PdfRenderRealtime()
        //{
        //    if (IsFirst)
        //    {
        //        IsFirst = false;
        //        try
        //        {
        //            if (System.IO.Directory.Exists(datapath))
        //                System.IO.Directory.Delete(datapath, true);
        //        }
        //        catch (Exception ex)
        //        {
        //            Console.WriteLine(ex.Message);
        //        }
        //    }
        //}
        /// <summary>
        /// 设置页面大小,系统会按照原始纸张长宽比例自动进行缩放操作
        /// </summary>
        /// <param name="newPageSize"></param>
        public void SetPageSize(SizeF newPageSize)
        {
            this.bitmapSize = newPageSize;
        }

        /// <summary>
        /// 当前页面，默认为空，只有调用了Goto*方法才会有效
        /// </summary>
        public string CurrentDocument
        {
            get { return currentDoc; }
        }
        private Bitmap currentPageBiamap = null;
        /// <summary>
        /// 当前页内容图片
        /// </summary>
        public Bitmap CurrentPageBitmap
        {
            get { return this.currentPageBiamap; }
        }
        /// <summary>
        /// 当前页码
        /// </summary>
        public int CurrentPage
        {
            get { return this.currentPage; }
        }
        public float CurrentScale
        {
            get { return PdfRender.CurrentScale; }
        }
        /// <summary>
        /// 文件总的页码
        /// </summary>
        public int PageCount
        {
            get { return this.pageCount; }
        }
        /// <summary>
        /// 打开pdf文件,使用默认A4纸张大小
        /// </summary>
        /// <param name="fileName"></param>
        public bool OpenFile(string fileName)
        {
            if (System.IO.File.Exists(fileName) == false)
            {
                throw new System.IO.FileNotFoundException("指定的文件不存在");
            }
            string ext = System.IO.Path.GetExtension(fileName);
            if (ext.ToLower() != ".pdf")
            {
                throw new Exception("该文件不是合法的pdf文件");
            }
            IntPtr ctx = IntPtr.Zero;
            IntPtr stm = IntPtr.Zero;
            IntPtr doc = IntPtr.Zero;
            try
            {
                ctx = NativeMethods.NewContext(IntPtr.Zero, IntPtr.Zero, FZ_STORE_DEFAULT); // Creates the context
                stm = NativeMethods.OpenFile(ctx, fileName); // opens pdf file as a stream
                doc = NativeMethods.OpenDocumentStream(ctx, ".pdf", stm); // opens the document
                int pn = NativeMethods.CountPages(doc); // gets the number of pages in the document

                this.context = ctx;
                this.fileStream = stm;
                this.documentStream = doc;
                this.pageCount = pn;

                return true;
            }
            catch
            {
                ctx = IntPtr.Zero;
                stm = IntPtr.Zero;
                doc = IntPtr.Zero;

                return false;
            }
        }
        /// <summary>
        /// 打开pdf文件,使用自定义纸张大小
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="pageSize"></param>
        public bool OpenFile(string fileName, SizeF pageSize)
        {
            if (OpenFile(fileName))
            {
                SetPageSize(pageSize);
                return true;
            }
            else
                return false;
        }

        public void GotoFirstPage()
        {
            SetCurrentPage(0);
        }
        /// <summary>
        /// 设置当前页
        /// </summary>
        /// <param name="page">page大于等于0并且page小于等于pageCount-1 </param>
        public void SetCurrentPage(int page)
        {
            //if (pageCacheDict.ContainsKey(page))
            //{
            //    //currentDoc = pageCacheDict[page];
            //    currentPageBiamap = pageCacheDict[page];
            //}
            //else
            //{
            if (page < 0 || page >= pageCount)
                return;

            currentPage = page;
            string bmpFileName = System.IO.Path.Combine(datapath, (page + 1) + ".png");
            Console.WriteLine("Rendering page " + (page + 1));
            IntPtr p = NativeMethods.LoadPage(documentStream, page); // loads the page (first page number is 0)
            Rectangle b = NativeMethods.BoundPage(documentStream, p); // gets the page size
            //using (var bmp = PdfRender.RenderPage(context, documentStream, p, b, bitmapSize))
            //{
            //    // renders the page and converts the result to Bitmap
            //    Console.WriteLine("Saving picture: " + bmpFileName);
            //    bmp.Save(bmpFileName); // saves the bitmap to a file
            //}
            var bmp = PdfRender.RenderPage(context, documentStream, p, b, bitmapSize);
            currentPageBiamap = bmp;

            NativeMethods.FreePage(documentStream, p); // releases the resources consumed by the page
            //currentDoc = bmpFileName;
            //pageCacheDict[page] = currentDoc;
            //pageCacheDict[page] = currentPageBiamap;
            //}
        }

        public void GotoLastPage()
        {
            SetCurrentPage(pageCount - 1);
        }

        public void GotoPreviousPage()
        {
            if (currentPage > 0)
                SetCurrentPage(--currentPage);
        }

        public void GotoNextPage()
        {
            if (currentPage < pageCount - 1)
                SetCurrentPage(++currentPage);
        }

        /// <summary>
        /// 长宽等比缩放
        /// </summary>
        /// <param name="percent">缩放比例</param>
        public void ScalePage(float percent)
        {
            if (percent == this.CurrentScale)
                return;
            //SizeF newSize = new SizeF(this.bitmapSize.Width * percent, this.bitmapSize.Height * percent);
            //    this.bitmapSize = newSize;

            IntPtr p = NativeMethods.LoadPage(documentStream, currentPage); // loads the page (first page number is 0)
            Rectangle b = NativeMethods.BoundPage(documentStream, p); // gets the page size

            var bmp = PdfRender.RenderPage(context, documentStream, p, b, percent);
            currentPageBiamap = bmp;
            this.bitmapSize = bmp.Size;

            NativeMethods.FreePage(documentStream, p); // releases the resources consumed by the page
            // pageCacheDict[currentPage] = currentPageBiamap;
        }

        void IDisposable.Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method. 
            // Therefore, you should call GC.SupressFinalize to 
            // take this object off the finalization queue 
            // and prevent finalization code for this object 
            // from executing a second time.
            GC.SuppressFinalize(this);
        }
        // Dispose(bool disposing) executes in two distinct scenarios. 
        // If disposing equals true, the method has been called directly 
        // or indirectly by a user's code. Managed and unmanaged resources 
        // can be disposed. 
        // If disposing equals false, the method has been called by the 
        // runtime from inside the finalizer and you should not reference 
        // other objects. Only unmanaged resources can be disposed. 
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called. 
            if (!this.disposed)
            {
                // If disposing equals true, dispose all managed 
                // and unmanaged resources. 
                if (disposing)
                {
                    // Dispose managed resources.        
                    foreach (KeyValuePair<int, Bitmap> item in this.pageCacheDict)
                    {
                        item.Value.Dispose();
                    }
                    this.pageCacheDict = null;
                    this.CurrentPageBitmap.Dispose();
                }
                // Call the appropriate methods to clean up 
                // unmanaged resources here. 
                // If disposing is false, 
                // only the following code is executed.
                NativeMethods.CloseDocument(documentStream); // releases the resources
                NativeMethods.CloseStream(fileStream);
                NativeMethods.FreeContext(context);

                disposed = true;
            }
        }

        public void Close()
        {
            (this as IDisposable).Dispose();
        }

        ~PdfRenderRealtime()
        {
            Dispose(false);
        }
    }
}
